package UserInterface;

import Audio.MusicPlayer;
import Engine.Character.BattleCharacter;
import Main.Tuxedo;
import javax.microedition.lcdui.Graphics;
import javax.microedition.lcdui.Image;
import javax.microedition.lcdui.game.GameCanvas;
import java.io.IOException;

/**
 * Painting routines and execution controlling class. Controls what is painted on the screen, the keyboard routine and the audio playing.
 * Main menu of game. It has the next options: New game, Continue, Highscores, Options and Exit.
 * @author Heldraug
 */
public class Menu extends GameCanvas {
    
    private Navigator navigator;
    private MenuMotion motion;
    private MusicPlayer music;
    private boolean effectStatus;
    private boolean goingDown;
    private boolean moving;
    private boolean defaultLanguage;
    private Graphics g;
    private int screenHeight;
    private int screenWidth;
    private final int Y_ANCHOR = 86;
    private final int CURSOR_MOVEMENT = 21;
    private int heightCursor;
    private int heightStop;
    private int keyStates;
    private int pastKeyStates;
    private int selectedOption;
    private int maxOptions;
    private int lastSelectedOption ;
    private Image[] imagesCommon;
    private Image[] imagesOptions;
    private int FPS;

    /**
     * Builds a new object containg every Image and routine needed to paint menus in the screen.
     * @param navigator Reference to the Navigator containing it.
     */
    public Menu(Navigator navigator) {
        super(true);
        this.setFullScreenMode(true);
        this.navigator = navigator;
        g = this.getGraphics();
        screenHeight = this.getHeight();
        screenWidth = this.getWidth();
        lastSelectedOption = 0;
        this.loadMain();
        music = new MusicPlayer(MusicPlayer.MENU_MUSIC);
        imagesCommon = new Image[3];
        imagesOptions = new Image[8];
        defaultLanguage = true;
        this.loadLanguageImages();
        this.loadCommonImages();
        //Objects should be as limited as possible. Every image should be in the same array.
        
        motion = new MenuMotion(this);
        motion.begin();
    }

    //Loads the images depending of the language
    private void loadLanguageImages() {
        try {
            if (defaultLanguage) {
                imagesOptions[0] = Image.createImage("/Images/UserInterface/Main-en.png");
                imagesOptions[1] = Image.createImage("/Images/UserInterface/NewGame-en.png");
                imagesOptions[2] = Image.createImage("/Images/UserInterface/Continue-en.png");
                imagesOptions[3] = Image.createImage("/Images/UserInterface/Options-en.png");
                //imagesOptions[4] = Image.createImage("/Images/UserInterface/HighScores.png");
                imagesOptions[5] = Image.createImage("/Images/UserInterface/Help.png");
                imagesOptions[6] = Image.createImage("/Images/UserInterface/Exit-en.png");
            } else {
                imagesOptions[0] = Image.createImage("/Images/UserInterface/Main-es.png");
                imagesOptions[1] = Image.createImage("/Images/UserInterface/NewGame-es.png");
                imagesOptions[2] = Image.createImage("/Images/UserInterface/Continue-es.png");
                imagesOptions[3] = Image.createImage("/Images/UserInterface/Options-es.png");
                //imagesOptions[4] = Image.createImage("/Images/UserInterface/HighScores.png");
                imagesOptions[5] = Image.createImage("/Images/UserInterface/Help.png");
                imagesOptions[6] = Image.createImage("/Images/UserInterface/Exit-es.png");
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    //Loads the common images like background and cursor
    private void loadCommonImages() {
        try {
            imagesCommon[0] = Image.createImage("/Images/UserInterface/Background.png");
            imagesCommon[1] = Image.createImage("/Images/UserInterface/Banner.png");
            imagesCommon[2] = Image.createImage("/Images/UserInterface/Cursor.png");
            imagesOptions[7] = Image.createImage("/Images/UserInterface/CharacterSelect.png");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

   public void toggleMusic() {
       music.toggleMusic();
   }

   public void resumeMusic() {
       music.resume();
   }

    /**
     * Delegates a click routine to the corresponding method using the current Menu handled by Navigator and the variable "selectedOption"
     * contained in Menu.
     */
    public void handleClick() {
        switch (navigator.getCurrentMenu()) {
            case Navigator.NEW_GAME:
                this.handleNewGame();
                break;
            case Navigator.NEW_GAME_CHARACTER:
                this.handleNewGameCharacter();
                break;
            case Navigator.CONTINUE:
                this.handleContinue();
                break;
            case Navigator.OPTIONS:
                this.handleOptions();
                break;
            case Navigator.HIGH_SCORES:
                this.loadMain();
                break;
            case Navigator.HELP:
                this.loadMain();
                break;
            case Navigator.EXIT:
                this.handleExit();
                break;
            case Navigator.MAIN:
                this.handleMain();
                break;
            default:
        }
    }

    /**
     * Sets the difficulty and passes to the main player selection screen and changes the MIDlet's display to GameDisplay, the GameCanvas
     * containing the screen of the game.
     */
    public void handleNewGame() {
        maxOptions = navigator.getMaxCurrentOptions(Navigator.NEW_GAME_CHARACTER);
        switch (selectedOption) {
            case 0:
                navigator.setCurrentMenu(Navigator.NEW_GAME_CHARACTER);
                this.navigator.setDifficulty(Tuxedo.EASY);
                break;
            case 1:
                navigator.setCurrentMenu(Navigator.NEW_GAME_CHARACTER);
                this.navigator.setDifficulty(Tuxedo.MEDIUM);
                break;
            case 2:
                navigator.setCurrentMenu(Navigator.NEW_GAME_CHARACTER);
                this.navigator.setDifficulty(Tuxedo.HARD);
                break;
        }
    }

    /**
     * Chooses a main player to be used in a new game, passing a Tuxedo integer index code representing the chosen character.
     */
    public void handleNewGameCharacter() {
        switch (selectedOption) {
            case BattleCharacter.LYN:
                navigator.setNewGameOptions(BattleCharacter.LYN);
                break;
            case BattleCharacter.ELIWOOD:
                navigator.setNewGameOptions(BattleCharacter.ELIWOOD);
                break;
            case BattleCharacter.EIRIK:
                navigator.setNewGameOptions(BattleCharacter.EIRIK);
                break;
        }
        navigator.switchDisplay(Tuxedo.GAME_SCREEN, true, Tuxedo.NO_FILE, defaultLanguage);
        this.shutDown(false);
    }

    /**
     * Loads the chosen file from an RMS source.
     */
    public void handleContinue() {
        switch (selectedOption) {
            case 0:
                navigator.switchDisplay(Tuxedo.GAME_SCREEN, false, Tuxedo.FILE_ONE, defaultLanguage);
                this.shutDown(false);
                break;
            case 1:
                //Load file 2
                //this.shutDown(false);
                break;
            case 2:
                //Load file 3
                //this.shutDown(false);
                break;
        }
    }

    /**
     * Controls the game's options, Sound, Vibration and Language.
     */
    public void handleOptions() {
        //A volume option should be added.
        switch (selectedOption) {
            case 0:
                this.music.toggleMusic();
                this.effectStatus = !effectStatus;
                break;
            case 1:
                this.defaultLanguage = ! this.defaultLanguage;
                this.loadLanguageImages();
                break;
        }
    }

    /**
     * Closes completely the application in case of confirmation, if not, returns to the main menu.
     */
    public void handleExit() {
        switch (selectedOption) {
            case 0:
                this.shutDown(true);
                break;
            case 1:
                this.loadMain();
                break;
        }
    }

    /**
     * Handles transition to the next menu when an option is clicked. For every option, it sets "selectedOption" to the first option available,
     * set "lastSelectedOption" to the option that was clicked in order to be selected if the user chooses to go back to the main menu and
     * asks the navigator the maximum number available of options in the next screen to paint the cursor and move it accurately.
     */
    public void handleMain() {
        switch (selectedOption) {
            case Navigator.NEW_GAME:
                selectedOption = 0;
                lastSelectedOption = Navigator.NEW_GAME;
                navigator.setCurrentMenu(Navigator.NEW_GAME);
                maxOptions = navigator.getMaxCurrentOptions(Navigator.NEW_GAME);
                break;
            case Navigator.CONTINUE:
                selectedOption = 0;
                lastSelectedOption = Navigator.CONTINUE;
                navigator.setCurrentMenu(Navigator.CONTINUE);
                maxOptions = navigator.getMaxCurrentOptions(Navigator.CONTINUE);
                break;
            case Navigator.OPTIONS:
                lastSelectedOption = Navigator.OPTIONS;
                selectedOption = 0;
                navigator.setCurrentMenu(Navigator.OPTIONS);
                maxOptions = navigator.getMaxCurrentOptions(Navigator.OPTIONS);
                break;
            case Navigator.HIGH_SCORES:
                selectedOption = 0;
                lastSelectedOption = Navigator.HIGH_SCORES;
                navigator.setCurrentMenu(Navigator.HIGH_SCORES);
                maxOptions = navigator.getMaxCurrentOptions(Navigator.HIGH_SCORES);
                break;
            case Navigator.HELP:
                selectedOption = 0;
                lastSelectedOption = Navigator.HELP;
                navigator.setCurrentMenu(Navigator.HELP);
                maxOptions = navigator.getMaxCurrentOptions(Navigator.HELP);
                break;
            case Navigator.EXIT:
                selectedOption = 0;
                lastSelectedOption = Navigator.EXIT;
                navigator.setCurrentMenu(Navigator.EXIT);
                maxOptions = navigator.getMaxCurrentOptions(Navigator.EXIT);
                break;
        }
    }

    /**
     * Paints the background on screen, containing a black rectangle spanning edge-to-edge on-screen, the background image and Chronicles of
     * Tuxedo logo.
     */
    public void paintCommon() {
        g.setColor(0x000000);
        g.fillRect(0, 0, screenWidth, screenHeight);
        g.drawImage(imagesCommon[0], screenWidth / 2, screenHeight / 2, Graphics.HCENTER | Graphics.VCENTER);
        g.drawImage(imagesCommon[1], screenWidth / 2, 10, Graphics.HCENTER | Graphics.TOP);
    }

    /**
     * Animates the cursor painting the path of it
     */
    public void paintCursor() {
        heightStop = Y_ANCHOR + (selectedOption * CURSOR_MOVEMENT);
        if (moving) {
            if (goingDown) {
                heightCursor += 3;
                if (heightCursor == heightStop + CURSOR_MOVEMENT) {
                    this.selectedOption++;
                    moving = false;
                }
            } else {
                heightCursor -= 3;
                if (heightCursor == heightStop - CURSOR_MOVEMENT) {
                    this.selectedOption--;
                    moving = false;
                }
            }
        } else {
            this.heightCursor = heightStop;
        }
        g.drawImage(imagesCommon[2], screenWidth / 4, heightCursor, Graphics.TOP | Graphics.RIGHT);
    }
 
    /**
     * Paints the FPS number contained in the variable "FPS" in the class, set previously by the method setFPS().
     * @deprecated
     * @see setFPS()
     */
    public void paintFPS() {
        g.setColor(0xFFFFFF);
        g.drawString(this.FPS + "", 5, 5, Graphics.TOP | Graphics.LEFT);
    }

    /**
     * Paints the screen with the menu needed. All images are referenced to the center of the screen, in order to mantain portability. In the
     * respective cases, calls paintCursor() to paint the cursor in the correct place. When finished, it double buffers the output using
     * flushGraphics().
     * @see paintCommon();
     * @see paintCursor();
     * @see paintFPS();
     */
    public void paint() {
        switch (navigator.getCurrentMenu()) {
            case Navigator.NEW_GAME:
                this.paintCursor();
                g.drawImage(imagesOptions[1], screenWidth / 2, Y_ANCHOR, Graphics.TOP | Graphics.HCENTER);
                break;
            case Navigator.NEW_GAME_CHARACTER:
                this.paintCursor();
                g.drawImage(imagesOptions[7], screenWidth / 2, Y_ANCHOR, Graphics.TOP | Graphics.HCENTER);
                break;
            case Navigator.CONTINUE:
                this.paintCursor();
                g.drawImage(imagesOptions[2], screenWidth / 2, Y_ANCHOR, Graphics.TOP | Graphics.HCENTER);
                break;
            case Navigator.OPTIONS:
                this.paintCursor();
                g.drawImage(imagesOptions[3], screenWidth / 2, Y_ANCHOR, Graphics.TOP | Graphics.HCENTER);
                break;
            case Navigator.HIGH_SCORES:
                break;
            case Navigator.HELP:
                g.drawImage(imagesOptions[5], screenWidth / 2, Y_ANCHOR, Graphics.TOP | Graphics.HCENTER);
                break;
            case Navigator.EXIT:
                this.paintCursor();
                g.drawImage(imagesOptions[6], screenWidth / 2, Y_ANCHOR, Graphics.TOP | Graphics.HCENTER);
                break;
            case Navigator.MAIN:
                this.paintCursor();
                g.drawImage(imagesOptions[0], screenWidth / 2, Y_ANCHOR, Graphics.TOP | Graphics.HCENTER);
                break;
        }
        this.flushGraphics();
    }

    /**
     * Routine to read the keyboard. Compares with each GameCanvas constant and with "pastKeyStates", to prevent a key event to be executed
     * multiple times. In case of a cursor movement with UP_PRESSED or DOWN_PRESSED, it calls the method play() in the corresponding
     * EffectsManager to sound the movement effect. With each click or selection with FIRE_PRESSED, it delegates execution to handleClick()
     * and calls the play() method in the corresponding EffectsManager to sound the click effect.
     */
    public void readKeyboard() {
        if (!moving) {
            keyStates = this.getKeyStates();
            if ((keyStates & GameCanvas.DOWN_PRESSED) != 0) {
                if (keyStates != pastKeyStates) {
                    pastKeyStates = keyStates;
                    if (selectedOption >= 0 && selectedOption < maxOptions - 1) {
                        heightCursor = Y_ANCHOR + (selectedOption * CURSOR_MOVEMENT);
                        this.goingDown = true;
                        this.moving = true;
                    } else if (selectedOption == maxOptions - 1) {
                        selectedOption = 0;
                        this.moving = false;
                    }
                }
            } else if ((keyStates & GameCanvas.UP_PRESSED) != 0) {
                if (keyStates != pastKeyStates) {
                    pastKeyStates = keyStates;
                    if (selectedOption <= maxOptions - 1 && selectedOption > 0) {
                        heightCursor = Y_ANCHOR + (selectedOption * CURSOR_MOVEMENT);
                        this.goingDown = false;
                        this.moving = true;
                    } else if (selectedOption == 0) {
                        selectedOption = maxOptions - 1;
                        this.moving = false;
                    }
                }
            } else if ((keyStates & GameCanvas.FIRE_PRESSED) != 0 | (keyStates & GameCanvas.RIGHT_PRESSED) != 0) {
                if (keyStates != pastKeyStates) {
                    pastKeyStates = keyStates;
                    this.handleClick();
                }
            } else if ((keyStates & GameCanvas.LEFT_PRESSED) != 0) {
                if (keyStates != pastKeyStates && navigator.getCurrentMenu() != Navigator.MAIN) {
                    this.loadMain();
                }
            } else {
                pastKeyStates = 0;
            }
        }
    }

    /**
     * Sets the FPS calculated in the Thread object MenuMotion to print on screen, for testing purposes only.
     * @param FPS
     * @see paintFPS();
     * @deprecated
     */
    public void setFPS(int FPS) {
        this.FPS = FPS;
    }

    /**
     * Executes the shut down sequence. Menu's shutDown() is called by the New Game handler, the Continue handler or the Exit handler.
     * @param exitFlag true to call destroyApp and terminate the application, false to destroy only the Navigator and start the game.
     */
    public void shutDown(boolean exitFlag) {
        motion.shutDown();
        music.shutDown();
        music = null;
        motion = null;
        if(motion == null)
            System.out.println("Menu succesfully destroyed.");
        navigator.shutDown(exitFlag);
    }

    /**
     * Sets the Navigator to the Main menu and "selectedOption" to the last option that was selected.
     */
    private void loadMain() {
        selectedOption = lastSelectedOption;
        navigator.setCurrentMenu(Navigator.MAIN);
        maxOptions = navigator.getMaxCurrentOptions(Navigator.MAIN);
    }
}